# -*- coding: utf-8 -*-
from dateutil import parser, relativedelta
import datetime, calendar, re
from random import randint

MIN_DATE = datetime.date(datetime.MINYEAR, 1, 1)
MAX_DATE = datetime.date(datetime.MAXYEAR, 12, 31)

BG_WEEKDAY = {1:"понеделник",2:"вторник",3:"сряда",4:"четвъртък",5:"петък",6:"събота",7:"неделя"}
BG_DAYS_SHORT = ('Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Нд')
BG_DAYS = ('понеделник', 'вторник', 'сряда', 'четвъртък', 'петък', 'събота', 'неделя')
BG_MONTHS = (None, 'януари', 'февруари', 'март', 'април', 'май', 'юни', 'юли', 'август', 'септември', 'октомври', 'ноември', 'декември')

def reldelta(*args, **kwargs):
	""" Just a shortcut for dateutil.relativedelta.relativedelta() """
	return relativedelta.relativedelta(*args, **kwargs)

def random_date(start_date=MIN_DATE, end_date=MAX_DATE):
	"""
	Returns a valid random date between start_date and end_date.  Both inputs and the output are datetime.date objects. If start_date is greater than end_date, the output is False.  
	"""
	if start_date > end_date:
		return False
	year = randint(start_date.year, end_date.year)
	mo_low = 1
	mo_high = 12
	if year==end_date.year:
		mo_high = end_date.month
	if year==start_date.year:
		mo_low = start_date.month
	month = randint(mo_low, mo_high)
	day_low = 1
	day_high = calendar.monthrange(year, month)[1]
	if (year==end_date.year) and (month==end_date.month):
		day_high = end_date.day
	if (year==start_date.year) and (month==start_date.month):
		day_low = start_date.day
	day = randint(day_low, day_high)
	return datetime.date(year, month, day)

def date2bg(dt_obj): 
	""" Formats a date object in BG date string.  """ 
	if isinstance(dt_obj, datetime.datetime): 
		return dt_obj.strftime("%d.%m.%Y %H:%M") 
	elif isinstance(dt_obj, datetime.date): 
		return dt_obj.strftime("%d.%m.%Y") 
	return dt_obj

def bg_date_full(d):
	try:
		return u"%s, %d %s %4dг." % (BG_DAYS[d.weekday()], d.day, BG_MONTHS[d.month], d.year)
	except:
		return None

def bg_date_short(d):
	try:
		return u"%s %d.%d" % (BG_DAYS_SHORT[d.weekday()], d.day, d.month)
	except:
		return None

def bg2date(date_str): 
	""" 
	Returns a datetime.datetime object from a BG date string.  Parses both date and time and ignores unrecognized tokens safely.  
	""" 
	return parser.parse(date_str, dayfirst=True, fuzzy=True)

def yesterday():
	return datetime.date.today() - datetime.timedelta(days=1)

def today():
	return datetime.date.today()

def tomorrow():
	return datetime.date.today() + datetime.timedelta(days=1)

def egn2bday(egn): 
	""" 
	Returns a datetime.datetime object with the person's birthday, given their EGN number.  
	""" 
	year, month, day = (int(egn[:2]), int(egn[2:4]), int(egn[4:6])) 
	if month > 40: 
		month -= 40 
		year += 2000 
	elif month > 20: 
		month -= 20 
		year += 1800 
	else: 
		year += 1900 
	return datetime.date(year, month, day)

def egn2age(egn, as_of=None): 
	""" 
	Returns the age of the person in years relative to the provided "as_of" date.  
	""" 
	if not as_of: 
		as_of = datetime.date.today() 
	return relativedelta.relativedelta(as_of, egn2bday(egn)).years

def week_of(dt):
	"""
	Given a datetime.date or datetime.datetime object, returns all dates of the week in which the input falls.  
	"""
	w = dt.weekday()
	return (dt + datetime.timedelta(days=i-w) for i in range(7))

def start_week(dt):
	"""
	Given a datetime.date or datetime.datetime object, returns a datetime.date object containing the first day of the week in which the input falls.  
	"""
	return (week_of(dt).next())

def end_week(dt):
	"""
	Given a datetime.date or datetime.datetime object, returns a datetime.date object containing the last day of the week in which the input falls.  
	"""
	return (start_week(dt) + datetime.timedelta(days=6))

def week_range(dt):
	return (startWeek(dt), endWeek(dt))

def month_of(dt):
	ms = datetime.date(dt.year, dt.month, 1)
	days = calendar.monthrange(dt.year, dt.month)[1]
	return (ms + datetime.timedelta(days=i) for i in range(days))

def start_month(dt):
	return datetime.date(dt.year, dt.month, 1)

def end_month(dt):
	return datetime.date(dt.year, dt.month, calendar.monthrange(dt.year, dt.month)[1])

def month_length(dt):
	return calendar.monthrange(dt.year, dt.month)[1]

def month_range(dt):
	return (startMonth(dt), endMonth(dt))

def date_range(startDate, endDate):
	"""
	A generator function returning all dates between startDate and endDate, inclusively. 
	"""
	return (startDate + datetime.timedelta(i) for i in range((endDate - startDate).days + 1))
